##
# AudienceStar module
# This file contains handlers for the basic features of AudienceStar,
# such as showing the index page and the main page for each Talk
##

import os

# Force Django to reload its settings.
#try:
#	from django.conf import settings
#	settings._target = None
#except:
#	pass
	
# Must set this env var before importing any part of Django
#os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

# This defines all our data types that are stored in Google's database
from astarclasses import *
from google.appengine.api import urlfetch
from appengine_utilities import sessions
from google.appengine.api import channel
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.api import images
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.api import mail
from datetime import datetime
from string import Template
import string
from string import *
import cgi
import logging
import sys
import random
import math
import re # regular expressions
import pickle
import time # for beer
from dateutil.relativedelta import relativedelta
from hashlib import sha256 # for storing passwords as hashes
from random import random
from django.utils import simplejson as json


#random_key = random()
#sha256('%s%s%s'%('YOUR SECRET KEY',random_key,password))

from astarutils import * # this defines helpful functions like friendly date delta format


class CreateNewTalk(BaseHandler):
	def get(self):
		#user, admin_user = self.user_and_admin()
		user = users.get_current_user()
		if not user:
			self.redirect(users.create_login_url(self.request.uri))
			return
		self.render_template("talk_create_page.html", {'user':user,
												'login_url':users.create_login_url('/'),
												'logout_url':users.create_logout_url("/")})
	
	def post(self):
		user = users.get_current_user()
		if not user:
			self.redirect(users.create_login_url(self.request.uri))
			return
		short_title = self.request.get("short_title")
		short_title = lower(short_title) # short title should always be lowercase
		talk = Talk(title=self.request.get("title"), short_title=short_title)
		talk.put()
		self.redirect("/admin/talk/edit/%s" % self.request.get("short_title"))
			

class EditTalk(BaseHandler):
	def get(self, talk_short_title):
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, show an error page
			self.render_template("invalid_talk.html", {'short_title':talk_short_title})
		else:
			topics = Topic.gql("WHERE ANCESTOR IS :1", talk)
			self.render_template("talk_edit_page.html", {'talk':talk, 'topics':topics})
			
	def post(self, talk_short_title):
		edited_talk = json.loads(self.request.get("talk_data"))
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, send an error message
			message = json.dumps({'error':"No such talk %s"%talk_short_title})
			self.response.out.write(message)
		else:
			# set the title
			talk.short_title = lower(edited_talk["short_title"])
			talk.title = edited_talk["title"]
			talk.description = edited_talk["description"]
			talk.icon_url = edited_talk["icon_url"]
			talk.category1 = edited_talk["category1"]
			talk.category2 = edited_talk["category2"]
			talk.category3 = edited_talk["category3"]
			talk.put()
			# update changed topics, add new topics, leave unchanged topics alone
			topic_keys = {}
			for edited_topic in edited_talk["topics"]:
				if (edited_topic["key"] != "no-key"):
					topic = Topic.get(edited_topic["key"])
				else:
					topic = None
				if topic:
					if topic.title != edited_topic["title"] or topic.description != edited_topic["description"]:
						topic.title = edited_topic["title"]
						topic.description = edited_topic["description"]
						topic.put()
				else:
					topic = Topic(parent=talk, title=edited_topic["title"], description=edited_topic["description"])
					topic.put()
				topic_keys[topic.key()] = topic
			# delete any removed topics from the datastore
			stored_topics = Topic.gql("WHERE ANCESTOR IS :1", talk)
			for stored_topic in stored_topics:
				if not topic_keys.has_key(stored_topic.key()):
					stored_topic.delete()
			message = json.dumps({'success':'ok'})
			self.response.out.write(message)
				
		
		
			
			
class PresenterPage(BaseHandler):
	def get(self, talk_short_title):
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, show an error page
			self.render_template("invalid_talk.html", {'short_title':talk_short_title})
		else:
			self.sess = sessions.Session() # get the unique session id to identify this client
			presenter = None # begin by assuming that no user (Presenter) has been created for this session
			if self.sess.has_key("presenter_key"): # try to find a star associated with this session
				presenter = Presenter.get(self.sess["presenter_key"])
			if not presenter: # if no presenter was found, create a new one now
				presenter = Presenter(parent=talk, talk=talk)
				presenter.put()
				# give the datastore time to submit the commit
				time.sleep(1)
				self.sess["presenter_key"] = presenter.key()
			if not (self.sess.has_key('token')): # check to see if we have a channel already open, if not, create one
				self.sess['token'] = channel.create_channel('presenter-'+str(presenter.key().id()))
			topics = Topic.gql("WHERE ANCESTOR IS :1", talk)
			unanswered_questions = db.GqlQuery("SELECT * FROM CommentQuestion WHERE type=:1 AND answered=:2 AND talk =:3 ORDER BY created_date", "question", False, talk)
			params= {
			'talk':talk,
			'token': self.sess['token'],
			'session_id': self.sess.sid,
			'presenter': presenter,
			'topics': topics,
			'unanswered_questions': unanswered_questions
			}
			
			self.render_template("talk_presenter_page.html", params)
	


class ChangeTalkState(BaseHandler):
	def get(self, talk_short_title):
		self.response.headers['Content-Type'] = 'text/plain'
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, show an error page
			self.response.out.write("error: No such talk '%s'" % (talk_short_title))
		else:
			new_state = self.request.get("new_state")
			if new_state in ("NOT_STARTED", "RANKING", "STARTED", "FINISHED", "ARCHIVED"):
				talk.state = new_state
				if (new_state == "STARTED"): # when we go to the talk page, always start with the #1 topic
					top_topic_query = db.GqlQuery("SELECT * FROM Topic WHERE ANCESTOR IS :1 ORDER BY avg_ranking DESC LIMIT 1", talk)
					top_topic = top_topic_query.get() # gets the highest-ranked (most interesting to audience) topic
					talk.current_topic = top_topic
				talk.put()
				# Now send an update message to all connected clients
				message_obj = {'type':'state_change', 'new_state':new_state}
				if (new_state == "STARTED" and talk.current_topic):
					message_obj['current_topic_title'] = talk.current_topic.title
					message_obj['current_topic_description'] = talk.current_topic.description
					message_obj['current_topic_id'] = talk.current_topic.key().id()
				message = json.dumps(message_obj) # encode message as json string
				connected_stars_query = db.GqlQuery("SELECT __key__ FROM Star WHERE ANCESTOR IS :1 AND connected=TRUE", talk)
				for connected_star_key in connected_stars_query:
					try:
						channel.send_message(str(connected_star_key.id()),message)
						#self.response.out.write("message sent to channel: %s, %s" %(str(connected_star_key.id()),message))
					except:
						logging.error("error sending update to star id %s" % str(connected_star_key.id))
						pass
				# now return response to presenter
				message_obj['success'] = 'ok';
				self.response.out.write(json.dumps(message_obj))
			else:
				message_obj['error'] = "Invalid state: '%s'" % (new_state)
				self.response.out.write(json.dumps(message_obj))
		
class ChangeTopic(BaseHandler):
	def get(self, talk_short_title):
		self.response.headers['Content-Type'] = 'text/plain'
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, show an error page
			self.response.out.write(json.dumps({"error": "No such talk '%s'" % (talk_short_title)}))
		else:
			direction = self.request.get("direction") # should be 'prev' or 'next' (anything else will stay on same topic and update clients)
			direction = lower(direction) # make it case insensitive 
			# get a list of all topics ranked from highest to lowest
			ranked_topics_query = db.GqlQuery("SELECT * FROM Topic WHERE ANCESTOR IS :1 ORDER BY avg_ranking DESC", talk)
			prev_topic = None
			next_topic = None
			found_current_topic = False
			for topic in ranked_topics_query:
				if found_current_topic: # if we just found the current topic last iteration
					next_topic = topic
					break # we've now found our current topic and the next topic, so stop looking
				if topic.key() == talk.current_topic.key(): # if this is our current topic
					found_current_topic = True
				else:
					prev_topic = topic # otherwise, remember this topic as the previous topic
			if (direction == "prev" and prev_topic is not None):
				talk.current_topic = prev_topic
				talk.put()
			elif (direction == "next" and next_topic is not None):
				talk.current_topic = next_topic
				talk.put()
			# Now send an update message to all connected clients
			message_obj = {'type':'new_topic'}
			if (talk.current_topic):
				message_obj['current_topic_title'] = talk.current_topic.title
				message_obj['current_topic_description'] = talk.current_topic.description
				message_obj['current_topic_id'] = talk.current_topic.key().id()
			message = json.dumps(message_obj) # encode message as json string
			connected_stars_query = db.GqlQuery("SELECT __key__ FROM Star WHERE ANCESTOR IS :1 AND connected=TRUE", talk)
			for connected_star_key in connected_stars_query:
				try:
					channel.send_message(str(connected_star_key.id()),message)
					#self.response.out.write("message sent to channel: %s, %s" %(str(connected_star_key.id()),message))
				except:
					pass
			self.response.out.write(message)


class UpdateDatastore(BaseHandler):
	def get(self):
		topics = Topic.all()
		for topic in topics:
			topic.put() # update schema
			self.response.out.write("updated topic:"+ topic.title + "<br/>")
		talks = Talk.all()
		for talk in talks:
			talk.put()
			self.response.out.write("updated talk:"+ talk.title + "<br/>")
		stars = Star.all()
		for star in stars:
			star.put()
		self.response.out.write("updated all stars<br/>")
		
		self.response.out.write("<h1>done</h1>")
		
class ResetTalkData(BaseHandler):
	def get(self, talk_short_title):
		self.response.headers['Content-Type'] = 'text/plain'
		talk_query = Talk.gql("WHERE short_title = :1", lower(talk_short_title))
		talk = talk_query.get()
		if not talk: # If the short_title given doesn't match any valid Talks, show an error page
			self.response.out.write("error: No such talk '%s'" % (talk_short_title))
		topics = Topic.gql("WHERE ANCESTOR IS :1", talk)
		for topic in topics:
			topic.num_rankings = 0
			topic.avg_ranking = float(0)
			topic.num_very_interested = 0
			topic.num_ratings = 0
			topic.avg_rating = float(0)
			topic.put()
			self.response.out.write("reset topic rankings/ratings<br/>")
		rankings = TopicRanking.gql("WHERE ANCESTOR IS :1", talk)
		for ranking in rankings:
			self.response.out.write("deleting topic ranking...");
			ranking.delete();
		stars = Star.gql("WHERE ANCESTOR IS :1", talk)
		for star in stars:
			self.response.out.write("deleting star<br/>")
			star.delete()
		presenters = Presenter.gql("WHERE ANCESTOR IS :1", talk)
		for presenter in presenters:
			self.response.out.write("deleted presenter<br/>")
			presenter.delete()
		
class AjaxSuccessHandler(BaseHandler):
	def get(self, file_id):
		self.response.headers['Content-Type'] = 'text/plain'
		self.response.out.write('%s/file/%s' % (self.request.host_url, file_id))

	
application = webapp.WSGIApplication([('/admin/talk/create', CreateNewTalk),
					  ('/admin/talk/edit/([a-zA-Z0-9]+)', EditTalk),
					  ('/admin/talk/reset/([a-zA-Z0-9]+)', ResetTalkData),
					  ('/admin/go/([a-zA-Z0-9]+)', PresenterPage),
					  ('/admin/talk/change_state/([a-zA-Z0-9]+)', ChangeTalkState),
					  ('/admin/updatedb', UpdateDatastore),
					  ('/admin/talk/change_topic/([a-zA-Z0-9]+)', ChangeTopic),
					  ],
				     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()
